package com.black.cat.jvm.rtda.heap.constantpool;


import com.black.cat.jvm.classfile.constantpool.impl.ConstantInterfaceMethodRefInfo;
import com.black.cat.jvm.rtda.heap.methodarea.JClass;
import com.black.cat.jvm.rtda.heap.methodarea.JMethod;
import com.black.cat.jvm.rtda.heap.methodarea.MethodLookup;

public class InterfaceMethodRef extends SymRef {

    private JMethod method;

    public String name;
    public String descriptor;

    public static InterfaceMethodRef newInterfaceMethodRef(RunTimeConstantPool runTimeConstantPool, ConstantInterfaceMethodRefInfo refInfo) {
        InterfaceMethodRef ref = new InterfaceMethodRef();
        ref.runTimeConstantPool = runTimeConstantPool;
        ref.copyMemberRefInfo(refInfo);
        return ref;
    }

    public void copyMemberRefInfo(ConstantInterfaceMethodRefInfo refInfo) {
        this.className = refInfo.getClassName();
        String[] split = refInfo.getNameAndType().split(":");
        this.name = split[0];
        this.descriptor = split[1];
    }

    public JMethod resolvedInterfaceMethod() {
        if (this.method == null) {
            this.resolveInterfaceMethodRef();
        }
        return this.method;
    }

    private void resolveInterfaceMethodRef() {
        JClass d = this.runTimeConstantPool.getClazz();
        JClass c = this.resolvedClass();
        if (!c.isInterface()) {
            throw new IncompatibleClassChangeError();
        }

        JMethod method = lookupInterfaceMethod(c, this.name, this.descriptor);
        if (null == method) {
            throw new NoSuchMethodError();
        }

        if (!method.isAccessibleTo(d)){
            throw new IllegalAccessError();
        }

        this.method = method;
    }

    private JMethod lookupInterfaceMethod(JClass iface, String name, String descriptor) {
        for (JMethod method : iface.JMethods) {
            if (method.name.equals(name) && method.descriptor.equals(descriptor)) {
                return method;
            }
        }
        return MethodLookup.lookupMethodInInterfaces(iface.interfaces, name, descriptor);
    }



    public String getName() {
        return name;
    }

    public String getDescriptor() {
        return descriptor;
    }
}
